#include "Crate.h"
#include "Vertex.h"

namespace D3D10Utils
{
	Crate::Crate()
		: mD3DDevice(0)
		, mVertexBuffer(0)
		, mIndexBuffer(0)
		, mNumVertices(0)
		, mNumFaces(0)
	{
	}
 
	Crate::~Crate()
	{
		ReleaseCOM(mVertexBuffer);
		ReleaseCOM(mIndexBuffer);
	}

	void Crate::init(ID3D10Device * const device, const float scale)
	{
		mD3DDevice = device;
 
		mNumVertices = 24;
		mNumFaces = 12; // 2 per quad

		// Create vertex buffer
		Vertex vertices[24]; 

		// Fill in the front face vertex data.
		vertices[0] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
		vertices[1] = Vertex(-1.0f,  1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
		vertices[2] = Vertex( 1.0f,  1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
		vertices[3] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);

		// Fill in the back face vertex data.
		vertices[4] = Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
		vertices[5] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f);
		vertices[6] = Vertex( 1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
		vertices[7] = Vertex(-1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f);

		// Fill in the top face vertex data.
		vertices[8]  = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
		vertices[9]  = Vertex(-1.0f, 1.0f,  1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
		vertices[10] = Vertex( 1.0f, 1.0f,  1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
		vertices[11] = Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);

		// Fill in the bottom face vertex data.
		vertices[12] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f);
		vertices[13] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f);
		vertices[14] = Vertex( 1.0f, -1.0f,  1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f);
		vertices[15] = Vertex(-1.0f, -1.0f,  1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);

		// Fill in the left face vertex data.
		vertices[16] = Vertex(-1.0f, -1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
		vertices[17] = Vertex(-1.0f,  1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
		vertices[18] = Vertex(-1.0f,  1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
		vertices[19] = Vertex(-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f);

		// Fill in the right face vertex data.
		vertices[20] = Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
		vertices[21] = Vertex( 1.0f,  1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
		vertices[22] = Vertex( 1.0f,  1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
		vertices[23] = Vertex( 1.0f, -1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
    	
		// Scale the box.
		for(DWORD i = 0; i < mNumVertices; ++i)
			vertices[i].mPos *= scale;

		// Description of the vertex buffer.
    	D3D10_BUFFER_DESC vertexBufferDescription;
    	vertexBufferDescription.ByteWidth = sizeof(Vertex) * mNumVertices;

		// A resource that is accessible by both the GPU and the CPU (write only). 
		// A dynamic resource is a good choice for a resource that will be updated 
		// by the CPU at least once per frame.
		vertexBufferDescription.Usage = D3D10_USAGE_DYNAMIC; 

    	// Bind a buffer as a vertex buffer to the input-assembler stage.
    	vertexBufferDescription.BindFlags = D3D10_BIND_VERTEX_BUFFER; 
		vertexBufferDescription.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; 
    	vertexBufferDescription.MiscFlags = 0; 	
		// Specifies data for initializing a subresource.
   	 	D3D10_SUBRESOURCE_DATA vertexBufferData;
    	vertexBufferData.pSysMem = vertices;

    	ErrorHandler(mD3DDevice->CreateBuffer(&vertexBufferDescription, &vertexBufferData, &mVertexBuffer));

		// Create the index buffer
		DWORD indices[36];

		// Fill in the front face index data
		indices[0] = 0; indices[1] = 1; indices[2] = 2;
		indices[3] = 0; indices[4] = 2; indices[5] = 3;

		// Fill in the back face index data
		indices[6] = 4; indices[7]  = 5; indices[8]  = 6;
		indices[9] = 4; indices[10] = 6; indices[11] = 7;

		// Fill in the top face index data
		indices[12] = 8; indices[13] =  9; indices[14] = 10;
		indices[15] = 8; indices[16] = 10; indices[17] = 11;

		// Fill in the bottom face index data
		indices[18] = 12; indices[19] = 13; indices[20] = 14;
		indices[21] = 12; indices[22] = 14; indices[23] = 15;

		// Fill in the left face index data
		indices[24] = 16; indices[25] = 17; indices[26] = 18;
		indices[27] = 16; indices[28] = 18; indices[29] = 19;

		// Fill in the right face index data
		indices[30] = 20; indices[31] = 21; indices[32] = 22;
		indices[33] = 20; indices[34] = 22; indices[35] = 23;

		// Description of the index buffer.
		D3D10_BUFFER_DESC indexBufferDescription;
		indexBufferDescription.ByteWidth = sizeof(DWORD) * mNumFaces * 3;
		
    	// A resource that can only be read by the GPU. 
    	// It cannot be written by the GPU, and cannot be accessed at all by the CPU. 
    	// This type of resource must be initialized when it is created, 
		// since it cannot be changed after creation.
    	indexBufferDescription.Usage = D3D10_USAGE_IMMUTABLE;
	
    	// Bind a buffer as a index buffer to the input-assembler stage.
    	indexBufferDescription.BindFlags = D3D10_BIND_INDEX_BUFFER;
    	indexBufferDescription.CPUAccessFlags = 0; // No CPU access necessary.
    	indexBufferDescription.MiscFlags = 0;

    	D3D10_SUBRESOURCE_DATA indexBufferData;
    	indexBufferData.pSysMem = indices;

    	ErrorHandler(mD3DDevice->CreateBuffer(&indexBufferDescription, &indexBufferData, &mIndexBuffer));
	}

	void Crate::draw()
	{
		const UINT stride = sizeof(Vertex);
    	const UINT offset = 0;
    	mD3DDevice->IASetVertexBuffers(0, 1, &mVertexBuffer, &stride, &offset);
		mD3DDevice->IASetIndexBuffer(mIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
		
		mD3DDevice->DrawIndexed(mNumFaces * 3, 0, 0);
	}
}
